﻿using System.Threading.RateLimiting;
using Asp.Versioning;
using Infrastructure.Configuration;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using WebApi.ConfigureOptions;

namespace WebApi.Extensions;

internal static class ServiceCollectionExtensions
{
    internal static IServiceCollection AddApiVersioningInternal(this IServiceCollection services)
    { 
        services.AddApiVersioning(options =>
        {
            options.AssumeDefaultVersionWhenUnspecified = true; //当未指定版本时，使用默认版本
            options.DefaultApiVersion = new ApiVersion(1, 0); //默认版本
            options.ReportApiVersions = true; //返回支持的版本信息
            options.ApiVersionReader = new UrlSegmentApiVersionReader();
            options.Policies.Sunset( 1.0 ) //配置Sunset
                .Effective( 2027, 6, 1 )
                .Link( "https://docs.api.com/policy.html?api-version=1.0" )
                .Title( "API Policy" )
                .Type( "text/html" );
        }).AddApiExplorer(options =>
        {
            options.GroupNameFormat = "'v'V";
            options.SubstituteApiVersionInUrl = true;
        });
        return services;
    }
    
    internal static IServiceCollection AddRateLimiting(this IServiceCollection services)
    {
        services.AddRateLimiter(options =>
        {
            // 配置默认策略
            options.AddPolicy("default", httpContext =>
            {
                string userName = httpContext.User.Identity?.Name ?? string.Empty;

                if (!string.IsNullOrEmpty(userName))
                {
                    return RateLimitPartition.GetTokenBucketLimiter(
                        userName,
                        _ => new TokenBucketRateLimiterOptions
                        {
                            TokenLimit = 100,
                            QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
                            QueueLimit = 5,
                            ReplenishmentPeriod = TimeSpan.FromMinutes(1),
                            TokensPerPeriod = 25
                        });
                }
                return RateLimitPartition.GetFixedWindowLimiter(
                    "anonymous",
                    _ => new FixedWindowRateLimiterOptions
                    {
                        PermitLimit = 5,
                        Window = TimeSpan.FromMinutes(1)
                    });
            });

            // 配置被拒绝请求的处理
            options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
            options.OnRejected = async (context, token) =>
            {
                if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out TimeSpan retryAfter))
                {
                    context.HttpContext.Response.Headers.RetryAfter = $"{retryAfter.TotalSeconds}";
                }

                ProblemDetailsFactory problemDetailFactory = context.HttpContext.RequestServices.GetRequiredService<ProblemDetailsFactory>();
                ProblemDetails problemDetails = problemDetailFactory.CreateProblemDetails(
                    context.HttpContext,
                    StatusCodes.Status429TooManyRequests,
                    title: "Too Many Requests",
                    detail: $"Too many requests. Please try again after {retryAfter.TotalSeconds} seconds."
                );
                await context.HttpContext.Response.WriteAsJsonAsync(problemDetails, cancellationToken: token);
            };
        });
        return services;
    }

    internal static IServiceCollection AddSwaggerGenInternal(this IServiceCollection services)
    {
        services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
        
        services.AddSwaggerGen(o =>
        {
            o.OperationFilter<SwaggerDefaultValues>();
            o.CustomSchemaIds(id => id.FullName!.Replace('+', '-'));

            var securityScheme = new OpenApiSecurityScheme
            {
                Name = "JWT Authentication",
                Description = "Enter your JWT token in this field",
                In = ParameterLocation.Header,
                Type = SecuritySchemeType.Http,
                Scheme = JwtBearerDefaults.AuthenticationScheme,
                BearerFormat = "JWT"
            };

            o.AddSecurityDefinition(JwtBearerDefaults.AuthenticationScheme, securityScheme);

            var securityRequirement = new OpenApiSecurityRequirement
            {
                {
                    new OpenApiSecurityScheme
                    {
                        Reference = new OpenApiReference
                        {
                            Type = ReferenceType.SecurityScheme,
                            Id = JwtBearerDefaults.AuthenticationScheme
                        }
                    },
                    []
                }
            };
            o.AddSecurityRequirement(securityRequirement);
        });

        return services;
    }

    internal static IServiceCollection AddCorsInternal(this IServiceCollection services, IConfiguration configuration)
    {
        // 配置CORS
        services.AddCors(options => options.AddPolicy("Default", policy =>
        {
            bool allowAnyOrigin = configuration.GetValue<bool>("Cors:AllowAnyOrigin");
            if (allowAnyOrigin)
            {
                policy.AllowAnyOrigin();
            }
            else
            {
                string[] allowedOrigins = configuration.GetSectionOrThrow<string[]>("Cors:AllowedOrigins");
                policy.WithOrigins(allowedOrigins);
            }

            policy.AllowAnyMethod()
                .AllowAnyHeader();
        }));
        return services;
    }
}
